package boardGenerator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

/**
 * Generador de tableros Sudokus Version2
 * 
 * Genera tableros a partir de mutuaciones aplicada en un tablero
 * dado, luego de eso oculta numeros de manera random siempre fijandose
 * que el tablero tenga unica solucion
 * 
 */
public class SudokuGenerator2 {

	private int[][] tablero;
        private int[][] tableroPartida;

	public SudokuGenerator2(GameDifficultyLevel diff) {
            tablero = new int[][]  {{1,2,3,4,5,6,7,8,9},
                                    {4,5,6,7,8,9,1,2,3},
                                    {7,8,9,1,2,3,4,5,6},
                                    {2,1,4,3,6,5,8,9,7},
                                    {3,6,5,8,9,7,2,1,4},
                                    {8,9,7,2,1,4,3,6,5},
                                    {5,3,1,6,4,2,9,7,8},
                                    {6,4,2,9,7,8,5,3,1},
                                    {9,7,8,5,3,1,6,4,2}};
            generarTablero();
            visibleNumber(diff.getDifficulty());           
	}

	/**
         * Genera el tablero Problema Aleatoriamente Segun Dificultad
         */
        private void generarTablero(){
            /**
             * Operaciones:
             * 0 = rotarTablero();
             * 1 = swapColumnBlock(A,B);
             * 2 = swapNumber(A,B);
             * 3 = swapColumn(A,B); A y B pertenecen al mismo Bloque 1,2,3
             * 0,1,2 = b.1 ; 3,4,5 = b.2; 6,7,8 = b.3;
             * Solo se pueden hacer swap de columnas del mismo bloque!!!
             */
            Random randomGenerator = new Random();
            ArrayList<Integer> test = new ArrayList<Integer>();
            for (int i=0;i<50;i++){test.add(randomGenerator.nextInt(4));}
            Collections.shuffle(test);
            for(Integer i:test){
                switch (i){
                    case 0: rotarTablero();break;
                    case 1: swapColumnBlock(randomGenerator.nextInt(3)+1,randomGenerator.nextInt(3)+1);break;
                    case 2: swapNumber(randomGenerator.nextInt(9)+1,randomGenerator.nextInt(9)+1);break;
                    case 3: swapColumn();break;
                }                
            }
	}

        /**
         * Rota el Tablero 90°
         */
        private void rotarTablero(){
            int[][] tableroTemp = new int[9][9];            
            for(int i=0;i<9;i++){
                for(int j=0;j<9;j++){
                    tableroTemp[i][j]=tablero[8-j][i];
                }
            }            
            tablero = tableroTemp;
        }        
        
        /**
         * Swap de Columnas validas random
         * Solo hace swap de columnas de un mismo bloque
         */
        private void swapColumn(){
            Random randomGenerator = new Random();
            int i = randomGenerator.nextInt(9);
            int j = randomGenerator.nextInt(3);
            if (i==0||i==1||i==2){swapColumn(i,j);return;}
            if (i==3||i==4||i==5){swapColumn(i,j+3);return;}
            if (i==6||i==7||i==8){swapColumn(i,j+6);return;}
        }
        
        /**
         * Intercambia Columnas
         * @param colA columna origen de 0 a 8
         * @param ColB columna destino de 0 a 8
         */
        private void swapColumn(int colA,int colB){
            //System.out.println("colA "+colA);
            //System.out.println("colB "+colB);
            //System.out.println("--------");
            for (int i=0;i<9;i++){
                int aux = tablero[i][colA];
                tablero[i][colA] = tablero[i][colB];
                tablero[i][colB] = aux;                
            }
        }
              
        /**
         * Intercambia un bloque de tres columnas por otro
         * bloque 1 columnas 0,1,2
         * bloque 2 columnas 3,4,5
         * bloque 3 columnas 6,7,8
         * @param blockA numero de bloqueA de 1 a 3
         * @param blockB numero de bloqueB de 1 a 3
         */
        private void swapColumnBlock(int blockA,int blockB){
            //System.out.println("bA "+blockA);
            //System.out.println("bB "+blockB);
            if ((blockA==1 && blockB==2)||(blockA==2 && blockB==1)){
                swapColumn(0,3);
                swapColumn(1,4);
                swapColumn(2,5);
            }
            if ((blockA==1 && blockB==3)||(blockA==3 && blockB==1)){
                swapColumn(0,6);
                swapColumn(1,7);
                swapColumn(2,8);
            }
            if ((blockA==2 && blockB==3)||(blockA==3 && blockB==2)){                
                swapColumn(3,6);
                swapColumn(4,7);
                swapColumn(5,8);
            }
        }        
         
        /**
         * Intercambia dos numeros en el tablero
         * cambia todas las apariciones de A por B y las de
         * B por A
         * @param numA primer numero
         * @param numB segundo numero
         */
        private void swapNumber(int numA,int numB){
            for(int i=0;i<9;i++){
                for(int j=0;j<9;j++){
                    if (tablero[i][j]==numA){tablero[i][j]=numB;} 
                    else {if(tablero[i][j]==numB){tablero[i][j]=numA;}};
                }
            }
        }        
        
        /**
         * Oculta Numeros en el tablero completo (solucion) para
         * asi generar el tablero problema, segun cantidad de numeros
         * a mostar.
         * @param n 
         */
        private void visibleNumber(int n){            
            tableroPartida = new int[9][9];
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    tableroPartida[i][j] = tablero[i][j];
                }                
            }            
            Random randomGenerator = new Random();
            int cont=0;
            ArrayList<Pair<Integer,Integer>> test = new ArrayList<Pair<Integer,Integer>>();
            do{
                int x = randomGenerator.nextInt(9);
                int y = randomGenerator.nextInt(9);
                Pair<Integer,Integer> tmp = new Pair<Integer,Integer>(x,y);
                if (!test.contains(tmp)){
                    int aux = tableroPartida[x][y];
                    tableroPartida[x][y] = 0;
                    if (!Solutions.hasUniqueSolution(tableroPartida)){
                        tableroPartida[x][y] = aux;
                    } else {
                        cont++;
                        test.add(tmp);
                    }
                }
            } while (cont<n);            
        }
        
        /**
         * Obtiene el tablero Solucion
         * @return 
         */
        public int[][] getSolution(){
            return tablero;
        }
        
        /**
         * Obtiene el tablero Problema
         * @return 
         */
        public int[][] getBoard(){        
            return tableroPartida;
        }
        
        /**
         * Obtiene la cantidad de numeros que muestra
         * @return 
         */
        public int getNumberCount(){
            return Utils.numberDisplay(this.tableroPartida);
        }
}